﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using Microsoft.Surface.Presentation.Input;
using ZOIL.Framework.Presentation;

namespace ZOIL.Library.Behaviors
{
    public static class ZObjectRotateBehavior
    {
        #region IsRotatable attached property (for setting rotation behavior active or unactive)

        public static DependencyProperty IsRotatableProperty = DependencyProperty.RegisterAttached("IsRotatable",
                                                                                                   typeof (bool),
                                                                                                   typeof (ZObjectRotateBehavior),
                                                                                                   new FrameworkPropertyMetadata(false,
                                                                                                                                 IsRotatableChanged));

        private static Dictionary<UIElement, ZObjectRotationInformation> ElementsBeingRotated =
            new Dictionary<UIElement, ZObjectRotationInformation>();

        #region mouse handling

        private static Adorner currentAdorner;
        private static AdornerLayer currentAdornerLayer;

        private static void OnMouseEnter(object sender, MouseEventArgs e)
        {
            var element = sender as ZComponent;

            if (element != null)
            {
                AdornerLayer layer = AdornerLayer.GetAdornerLayer(element);
                if (layer != null)
                {
                    var resizeAdorner = new ZObjectRotateAdorner(element);
                    layer.Add(resizeAdorner);
                }
            }
        }

        private static void OnMouseLeave(object sender, MouseEventArgs e)
        {
            var element = sender as UIElement;
            var comp = element as ZComponent;
            if (comp != null)
            {
                AdornerLayer layer = AdornerLayer.GetAdornerLayer(comp);
                if (layer != null)
                {
                    currentAdornerLayer = layer;
                    IEnumerable<ZObjectRotateAdorner> rotateAdorners = layer.GetAdorners(comp).OfType<ZObjectRotateAdorner>();
                    foreach (ZObjectRotateAdorner rotateAdorner in rotateAdorners)
                    {
                        if (rotateAdorner.AdornedElement == comp)
                        {
                            var test = new Rect(e.GetPosition(null).X, e.GetPosition(null).Y, comp.Width, comp.Height);
                            if (test.Contains(e.GetPosition(null)))
                            {
                                currentAdorner = rotateAdorner;
                                rotateAdorner.MouseLeave += rotateAdorner_MouseLeave;
                                return;
                            }
                            layer.Remove(rotateAdorner);
                        }
                    }
                }
            }
        }

        private static void rotateAdorner_MouseLeave(object sender, MouseEventArgs e)
        {
            currentAdornerLayer.Remove(currentAdorner);
        }

        #endregion

        #region surface handling

        private static void OnContactDown(object sender, TouchEventArgs e)
        {
        }

        private static void OnContactChanged(object sender, TouchEventArgs e)
        {
        }

        private static void OnContactLeave(object sender, TouchEventArgs e)
        {
        }

        #endregion

        private static void IsRotatableChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            var element = target as UIElement;

            /* MZ: Temporarily commented out due to performance issues
            ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, Mouse.MouseEnterEvent, new MouseEventHandler(OnMouseEnter));
            ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, Mouse.MouseLeaveEvent, new MouseEventHandler(OnMouseLeave));
            */
            //Surface
            //ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.TouchDownEvent, new EventHandler<TouchEventArgs>(ZObjectSurfaceHandler.OnContactDown));
            //ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.TouchUpEvent, new EventHandler<TouchEventArgs>(ZObjectSurfaceHandler.OnContactUp));
            //ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, Contacts.ContactChangedEvent, new ContactEventHandler(OnContactChanged));
            //ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, Contacts.ContactLeaveEvent, new ContactEventHandler(OnContactLeave));
            //ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, TouchExtensions.HoldGestureEvent, new EventHandler<TouchEventArgs>(ZObjectSurfaceHandler.OnContactHold));

            //TODO: besseren Weg finden um 2x/3x Registrierung der Events zu verhindern
            if (!ZObjectResizeBehavior.GetIsResizable(target as UIElement) && !ZObjectDragDropBehavior.GetIsDraggable(target as ZComponent))
            {
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, TouchExtensions.TapGestureEvent,
                                       new EventHandler<TouchEventArgs>(ZObjectSurfaceHandler.OnContactTapGesture));
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.ManipulationStartingEvent,
                                       new EventHandler<ManipulationStartingEventArgs>(ZObjectSurface2Handler.ManipulationStarting));
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.ManipulationDeltaEvent,
                                       new EventHandler<ManipulationDeltaEventArgs>(ZObjectSurface2Handler.ManipulationDelta));
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.ManipulationInertiaStartingEvent,
                                       new EventHandler<ManipulationInertiaStartingEventArgs>(ZObjectSurface2Handler.ManipulationInertiaStarting));
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.ManipulationCompletedEvent,
                                       new EventHandler<ManipulationCompletedEventArgs>(ZObjectSurface2Handler.ManipulationCompleted));
                ZBehavior.HandleChange(e.OldValue, e.NewValue, target as UIElement, UIElement.TouchDownEvent,
                                       new EventHandler<TouchEventArgs>(ZObjectSurface2Handler.OnTouchDown));
            }

            //Wii
        }

        public static void SetIsRotatable(UIElement target, bool value)
        {
            target.SetValue(IsRotatableProperty, value);
        }

        public static bool GetIsRotatable(UIElement d)
        {
            return (bool) d.GetValue(IsRotatableProperty);
        }

        #endregion
    }

    public class ZObjectRotationInformation
    {
        public double Bottom;
        public UIElement ElementBeingDragged;
        public bool IsDragging;
        public double Left;
        public Point OriginalMousePositionOnUIElement;
        public Point OriginalMousePositionOnWindow;
        public List<ZInformationLandscape> ParentLandscapes;
        public double Right;
        public double Top;

        public ZObjectRotationInformation(Point originalMousePosition)
        {
            OriginalMousePositionOnWindow = originalMousePosition;
        }

        public double getScaleFactor()
        {
            if (ParentLandscapes == null)
            {
                return 1.0;
            }
            else
            {
                double scale = 1.0;
                foreach (ZInformationLandscape landscape in ParentLandscapes)
                {
                    scale *= landscape.Animation.Position.Zoom;
                }
                return scale;
            }
        }
    }

    public class ZObjectRotateAdorner : ZComponentAdorner
    {
        private readonly Thumb _bottomLeft;
        private readonly Thumb _bottomRight;
        private readonly Thumb _topLeft;
        private readonly Thumb _topRight;

        private Point _centerPoint;
        private Vector _startVector;

        public ZObjectRotateAdorner(ZComponent adornedElement)
            : base(adornedElement)
        {
            //TODO: this is a mean hack to avoid adorners
            return;

            _topLeft = getAdornerCorner(Cursors.Cross, 10, 10);
            _topRight = getAdornerCorner(Cursors.Cross, 10, 10);
            _bottomLeft = getAdornerCorner(Cursors.Cross, 10, 10);
            _bottomRight = getAdornerCorner(Cursors.Cross, 10, 10);

            AddChild(_topLeft, AdornerPlacement.Out, AdornerPosition.TopLeftCorner);
            AddChild(_topRight, AdornerPlacement.CenterOnEdge, AdornerPosition.TopRightCorner);
            AddChild(_bottomLeft, AdornerPlacement.CenterOnEdge, AdornerPosition.BottomLeftCorner);
            AddChild(_bottomRight, AdornerPlacement.CenterOnEdge, AdornerPosition.BottomRightcorner);

            _topLeft.DragStarted += new DragStartedEventHandler(OnDragStarted);
            _topLeft.DragDelta += new DragDeltaEventHandler(OnDragDelta);
            _topRight.DragStarted += new DragStartedEventHandler(OnDragStarted);
            _topRight.DragDelta += new DragDeltaEventHandler(OnDragDelta);
            _bottomLeft.DragStarted += new DragStartedEventHandler(OnDragStarted);
            _bottomLeft.DragDelta += new DragDeltaEventHandler(OnDragDelta);
            _bottomRight.DragStarted += new DragStartedEventHandler(OnDragStarted);
            _bottomRight.DragDelta += new DragDeltaEventHandler(OnDragDelta);
        }

        private void OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            Vector currentVector = Point.Subtract(_centerPoint, Mouse.GetPosition(null));
            double angle = Vector.AngleBetween(_startVector, currentVector);
            AdornedComponent.Angle += angle;
        }

        private void OnDragStarted(object sender, DragStartedEventArgs e)
        {
            var centerPointInternal = new Point(AdornedComponent.Width/2.0, AdornedComponent.Height/2.0);
            _centerPoint = (sender as UIElement).TranslatePoint(centerPointInternal, Window.GetWindow(sender as UIElement));
            _startVector = Point.Subtract(_centerPoint, Mouse.GetPosition(null));
        }

        private Thumb getAdornerCorner(Cursor customizedCursor, double w, double h)
        {
            var cornerThumb = new Thumb();
            cornerThumb.Cursor = customizedCursor;
            cornerThumb.Width = (w < 0 ? 0 : w);
            cornerThumb.Height = (h < 0 ? 0 : h);

            return cornerThumb;
        }
    }
}